package com.tambapps.p2p.fandem

import com.tambapps.p2p.fandem.handshake.FandemSenderHandshake
import com.tambapps.p2p.fandem.handshake.SenderHandshakeData
import com.tambapps.p2p.fandem.model.SendingFileData
import com.tambapps.p2p.fandem.util.TransferListener
import com.tambapps.p2p.speer.Peer
import com.tambapps.p2p.speer.PeerConnection
import com.tambapps.p2p.speer.PeerServer
import com.tambapps.p2p.speer.handshake.Handshake

import java.io.File
import java.io.IOException
import java.util.concurrent.atomic.AtomicReference

class FileSender @JvmOverloads constructor(
    peer: Peer,
    listener: TransferListener? = null,
    socketTimeout: Int = 0
) : FileSharer(listener) {

    private val peer: Peer


    private val socketTimeout: Int
    private val connectionReference: AtomicReference<PeerConnection?> =
        AtomicReference<PeerConnection?>()

    init {
        this.peer = peer
        this.socketTimeout = socketTimeout
    }

    @Throws(IOException::class)
    fun sendFiles(files: List<File>) {
        val sendingFileDataList: MutableList<SendingFileData> = ArrayList<SendingFileData>()
        for (file in files) {
            sendingFileDataList.add(SendingFileData.fromFile(file))
        }
        send(sendingFileDataList)
    }

    @Throws(IOException::class)
    fun send(files: List<SendingFileData>) {
        peerServer(
            FandemSenderHandshake(SenderHandshakeData(files))
        ).use { server ->
            server.accept().use { connection ->
                connectionReference.set(connection)
                listener?.onConnected(connection.selfPeer, connection.remotePeer)
                for (fileData in files) {
                    val fileName: String = fileData.fileName!!
                    val fileSize: Long = fileData.fileSize
                    listener?.onTransferStarted(fileName, fileSize)
                    fileData.newInputStream()!!.use { inputStream ->
                        share(
                            inputStream,
                            connection.outputStream,
                            DEFAULT_BUFFER_SIZE,
                            fileName,
                            fileSize,
                            fileData.getChecksum().orElse(null)
                        )
                    }
                }
            }
        }
        connectionReference.set(null)
    }

    fun cancel() {
        val connection: PeerConnection? = connectionReference.get()
        if (connection != null) {
            try {
                connection.close()
            } catch (e: IOException) {
                // ignore
            }
            connectionReference.set(null)
        }
    }

    @Throws(IOException::class)
    private fun peerServer(handshake: Handshake): PeerServer {
        val server = PeerServer(peer, handshake)
        server.setAcceptTimeout(socketTimeout)
        return server
    }
}
